/*
 * File Name：CWCamera.java
 * Copyright：Copyright 2008-2013 CiWong.Inc. All Rights Reserved.
 * Description： CWCamera.java
 * Modify By：PLA-ZJLIU
 * Modify Date：2013-3-27
 * Modify Type：Add
 */
package com.ciwong.mobilelib.widget;


import java.io.IOException;
import java.lang.reflect.Method;
import java.util.List;

import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.PixelFormat;
import android.hardware.Camera;
import android.hardware.Camera.CameraInfo;
import android.hardware.Camera.PictureCallback;
import android.hardware.Camera.ShutterCallback;
import android.hardware.Camera.Size;
import android.os.Build;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.WindowManager;

import com.ciwong.libs.utils.CWLog;
import com.ciwong.libs.utils.ImageUtils;
import com.ciwong.libs.widget.CWToast;
import com.ciwong.mobilelib.R;
import com.ciwong.mobilelib.utils.BaseConstants;

/**
 * 
 * 拍照相机类
 * 
 * @author PLA-ZJLIU
 * @version ciwong v.1.0 2013-3-27
 * @since ciwong v.1.0
 */
public class CWCamera
{
    private static CWCamera mInstance;

    private static final String TAG = "camera";

    private Camera mCamera;

    private WindowManager mWindowManager;

    private ContentResolver mContentResolver;

    private TakePic mTakePic;

    private Size picRatio;

    private Size preRatio;

    private List<Size> suportDimSize;

    private List<Size> supportPreSize;

    private SurfaceHolder mHolder;

    private boolean isInit = false;

    public int rotation = 90;

    /**
     * 当前是否是预览
     */
    private boolean mIsPreview;

    private String flashlightMode;

    private ShutterCallback shutterCallback = new ShutterCallback()
    {
        public void onShutter()
        {
            Log.d(TAG, "onShutter'd");
        }
    };

    /** Handles data for raw picture */
    private PictureCallback rawCallback = new PictureCallback()
    {
        public void onPictureTaken(byte[] data, Camera camera)
        {
            Log.d(TAG, "onPictureTaken - raw");
        }
    };

    private PictureCallback jpegCallback = new PictureCallback()
    {
        public void onPictureTaken(byte[] data, Camera camera)
        {
            try
            {
                Bitmap bitmapPicture = null;
                if (data != null)
                {
                    bitmapPicture = ImageUtils.getImage(data,
                            BaseConstants.MATRIX_IMG_MAX_HEIGHT);
                    // bitmapPicture = ImageUtils.getImage(data,
                    // Constants.MATRIX_IMG_MAX_HEIGHT);
                }
                if (mTakePic != null)
                {
                    mTakePic.takePic(bitmapPicture);
                    mTakePic.takePic(data);
                }
            }
            catch (Exception ex)
            {
                ex.printStackTrace();
            }
            Log.d(TAG, "onPictureTaken - jpeg");
        }
    };

    private CWCamera()
    {
        flashlightMode = Camera.Parameters.FLASH_MODE_AUTO;
    }

    /**
     * 获取相机实例
     * 
     * @return {@link CWCamera}
     */
    public static CWCamera instance()
    {
        if (mInstance == null)
        {
            mInstance = new CWCamera();

        }
        return mInstance;
    }

    /**
     * 获取相机支持的分辨率
     * 
     * @return List<{@link Size}>
     */
    public List<Size> getSuportDimSize()
    {
        return suportDimSize;
    }

    /**
     * 
     * 获取相机实例
     * 
     * @return 相机
     */
    public Camera getCamera()
    {
        return mCamera;
    }

    /**
     * 设置{@link WindowManager}
     * 
     * @param windowManager
     *            {@link WindowManager}
     */
    public void setWindowManager(WindowManager windowManager)
    {
        mWindowManager = windowManager;
    }

    /**
     * 调用此方法后,拍照后会将数据插入到媒体库中
     * 
     * @param contentResolver
     *            {@link ContentResolver}
     */
    public void setContentResolver(ContentResolver contentResolver)
    {
        mContentResolver = contentResolver;
    }

    /**
     * 初始化相机
     * 
     * @param holder
     *            {@link SurfaceHolder}
     */
    public void initialPreview(SurfaceHolder holder)
    {
        try
        {
            if (mCamera != null)
            {
                mCamera.setPreviewDisplay(holder);
                isInit = true;
            }
            else
            {
                this.mHolder = holder;
            }
        }
        catch (Exception ex)
        {
            ex.printStackTrace();
        }
    }

    public boolean isInit()
    {
        return isInit;
    }

    /**
     * 设置输出文件格式
     * 
     * @param format
     *            图片输出格式,请查看类{@link PixelFormat}
     */
    public void setPicFormat(int format)
    {
        if (mCamera != null)
        {
            mCamera.getParameters().setPictureFormat(format);
        }
    }

    /**
     * 设置图片分辨率
     * 
     * @param picRatio
     *            分辨率 {@link Size}
     */
    public void setPicRatio(Size picRatio)
    {
        this.picRatio = picRatio;
        boolean isSupportRatio = false;
        for (Size size : suportDimSize)
        {
            if (size.width == picRatio.width && size.height == picRatio.height)
            {
                isSupportRatio = true;
                break;
            }
        }
        if (!isSupportRatio)
        {
            throw new UnsupportRatioException("不支持的分辨率");
        }
    }

    /**
     * 
     * 设置图片分辨率
     * 
     * @param w
     *            宽
     * @param h
     *            高
     */
    public void setPicRatio(int w, int h)
    {
        Size ratio = mCamera.getParameters().getPictureSize();
        ratio.height = h;
        ratio.width = w;
        setPicRatio(ratio);
    }

    /**
     * 获取当前分辨率
     * 
     * @return {@link Size}
     */
    public Size getPicRatio()
    {
        return picRatio;
    }

    /**
     * 设置预览框大小
     * 
     * @param width
     *            宽
     * @param height
     *            高
     */
    public void setPreRatio(int width, int height)
    {
        preRatio = mCamera.getParameters().getPreviewSize();
        preRatio.height = height;
        preRatio.width = width;
    }

    /**
     * 
     * 开始预览
     * 
     * @param context
     *            上下文
     */
    public void startPreview(final Context context)
    {
        new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                try
                {
                    if (preRatio != null)
                    {
                        CWLog.e(TAG, "startPreview()");
                        startPreview(preRatio.width, preRatio.height, context);
                    }
                }
                catch (Exception e)
                {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    /**
     * 开始预览
     * 
     * @param width
     *            宽
     * @param height
     *            高
     * @param context
     *            上下文
     */
    public synchronized void startPreview(int width, int height, Context context)
    {
        /**
         * sdk版本号
         */
        final int versionSdk = 8;
        /**
         * 图片质量
         */
        final int quality = 100;

        if (mIsPreview)
        {
            return;
        }
        CWLog.e(TAG, "STARTpREVIEW(int width, int height)");
        open();
        if (!isInit)
        {
            try
            {
                mCamera.setPreviewDisplay(mHolder);
            }
            catch (IOException e)
            {
                CWToast.makeText(context, R.string.connect_camera_failed,
                        CWToast.LENGTH_SHORT);
                e.printStackTrace();
            }
            isInit = true;
        }
        if (mCamera != null)
        {
            Camera.Parameters parameters = mCamera.getParameters();
            parameters.setFlashMode(flashlightMode);
            parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);

            preRatio = getOptimalPreviewSize(supportPreSize, width, height);
            picRatio = getOptimalPreviewSize(suportDimSize, width, height);
            parameters.setPreviewSize(preRatio.width, preRatio.height);
            parameters.setPictureSize(picRatio.width, picRatio.height);
            parameters.setPictureFormat(PixelFormat.JPEG);
            parameters.setJpegQuality(quality);
            try
            {
                if (Integer.parseInt(Build.VERSION.SDK) >= versionSdk)
                {
                    Method downPolymorphic = mCamera.getClass().getMethod(
                            "setDisplayOrientation", new Class[]{int.class});
                    if (downPolymorphic != null)
                    {
                        downPolymorphic.invoke(mCamera, new Object[]{rotation});
                    }
                }
                else
                {
                    if (context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT)
                    {
                        parameters.set("orientation", "portrait");
                        parameters.set("rotation", rotation);
                    }
                    if (context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE)
                    {
                        parameters.set("orientation", "landscape");
                        parameters.set("rotation", rotation);
                    }
                }
                mCamera.setParameters(parameters);
            }
            catch (Exception e1)
            {
                parameters.set("orientation", "portrait");
                e1.printStackTrace();
            }
            mCamera.startPreview();
            mIsPreview = true;
        }
    }

    private Size findBestPreviewSizeValue(List<Size> sizes, int w, int h)
    {
        int bestX = 0;
        int bestY = 0;
        int diff = Integer.MAX_VALUE;
        for (Size size : sizes)
        {

            int newX = size.width;
            int newY = size.height;
            int newDiff = 0;
            newDiff = Math.abs(newX - w) + Math.abs(newY - h);
            if (newDiff == 0)
            {
                bestX = newX;
                bestY = newY;
                break;
            }
            else if (newDiff < diff)
            {
                bestX = newX;
                bestY = newY;
                diff = newDiff;
            }

        }
        if (bestX > 0 && bestY > 0)
        {
            return mCamera.new Size(bestX, bestY);
        }
        return null;
    }

    private Size getOptimalPreviewSize(List<Size> sizes, int w, int h)
    {
        int tmp = h;
        if (w < h)
        {
            h = w;
            w = tmp;
        }
        final double aspectTolerance = 0.1;
        double targetRatio = (double) w / h;
        if (sizes == null)
        {
            return null;
        }
        Size optimalSize = null;
        double minDiff = Double.MAX_VALUE;

        int targetHeight = h;

        // Try to find an size match aspect ratio and size
        for (Size size : sizes)
        {
            double ratio = (double) size.width / size.height;
            if (Math.abs(ratio - targetRatio) > aspectTolerance)
            {
                continue;
            }
            if (Math.abs(size.height - targetHeight) < minDiff)
            {
                optimalSize = size;
                minDiff = Math.abs(size.height - targetHeight);
            }
        }

        // Cannot find the one match the aspect ratio, ignore the requirement
        if (optimalSize == null)
        {
            minDiff = Double.MAX_VALUE;
            for (Size size : sizes)
            {
                if (Math.abs(size.height - targetHeight) < minDiff)
                {
                    optimalSize = size;
                    minDiff = Math.abs(size.height - targetHeight);
                }
            }
        }
        return optimalSize;
    }

    /**
     * 停止预览
     * 
     */
    public void stopPreview()
    {
        CWLog.d(TAG, "stopPreview   mCamera:" + mCamera + "      isPreview:"
                + mIsPreview);
        if (mCamera != null && mIsPreview)
        {
            mCamera.stopPreview();
            // release();
        }
        mIsPreview = false;
    }

    /**
     * 拍照回调方法
     * 
     * @param mTakPic
     *            {@link TakePic}
     */
    public void setTakePicCallback(TakePic mTakPic)
    {
        this.mTakePic = mTakPic;
    }

    /**
     * 
     * 拍照<br />
     * <b style="color:red">拍照前如需获取照片则需要设置{@link TakePic}</b>
     * 
     */
    public void takePicture()
    {
        if (mCamera != null && isInit)
        {
            try
            {
                mCamera.takePicture(shutterCallback, rawCallback, jpegCallback);
            }
            catch (Exception e)
            {
                if (mTakePic != null)
                {
                    mTakePic.failed();
                }
                e.printStackTrace();
            }
        }
        else
        {
            if (mTakePic != null)
            {
                mTakePic.failed();
            }
        }
    }

    private void setFlashMode()
    {
        try
        {
            Camera.Parameters mParameters;
            mParameters = mCamera.getParameters();
            mParameters.setFlashMode(flashlightMode);
            mCamera.setParameters(mParameters);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    /**
     * 设置闪关灯模式
     * 
     * @param flashMode
     *            闪关灯模式
     * @see Camera.Parameters#FLASH_MODE_OFF
     * @see Camera.Parameters.FLASH_MODE_AUTO
     * @see Camera.Parameters.FLASH_MODE_ON
     * @see Camera.Parameters.FLASH_MODE_RED_EYE
     * @see Camera.Parameters.FLASH_MODE_TORCH
     */
    public void setFlashMode(String flashMode)
    {
        flashlightMode = flashMode;
        if (mCamera == null)
        {
            return;
        }
        setFlashMode();
    }

    /**
     * 打开相机
     * 
     * @return 是否打开相机
     */
    public boolean open()
    {
        CWLog.d(TAG, "stopPreview   mCamera:" + mCamera + "      isPreview:"
                + mIsPreview);
        try
        {
            if (mCamera == null)
            {
                mCamera = Camera.open();
                suportDimSize = mCamera.getParameters()
                        .getSupportedPictureSizes();
                supportPreSize = mCamera.getParameters()
                        .getSupportedPreviewSizes();
                picRatio = suportDimSize.get(suportDimSize.size() - 1);
                setFlashMode();
            }
            return true;
        }
        catch (Exception e)
        {
            e.printStackTrace();
            return false;
        }

    }

    /**
     * 是否能够打开前置摄像头
     * 
     * @return 是否能够打开前置摄像头
     */
    public boolean canOpenFacing()
    {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD
                && Camera.getNumberOfCameras() > 1;
    }

    /**
     * 打开前置摄像头
     * 
     * @return 打开是否成功
     */
    public boolean openFacing()
    {
        try
        {
            if (mCamera != null)
            {
                return true;
            }
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD)
            {
                for (int i = 0; i < Camera.getNumberOfCameras(); i++)
                {
                    CameraInfo info = new CameraInfo();
                    Camera.getCameraInfo(i, info);
                    if (info.facing == CameraInfo.CAMERA_FACING_FRONT)
                    {
                        mCamera = Camera.open(i);
                        setFlashMode();
                        suportDimSize = mCamera.getParameters()
                                .getSupportedPictureSizes();
                        supportPreSize = mCamera.getParameters()
                                .getSupportedPreviewSizes();
                        picRatio = suportDimSize.get(suportDimSize.size() - 1);
                        return true;
                    }
                }
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 释放资源
     * 
     */
    public void release()
    {
        if (mCamera != null)
        {
            try
            {
                mCamera.stopPreview();
                mCamera.release();
                mCamera = null;
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }

        }
        isInit = false;
        mIsPreview = false;
    }

    /**
     * 拍照类
     * 
     * @author res-qqlin
     * 
     */
    public abstract static class TakePic
    {
        /**
         * 拍照
         * 
         * @param mBitmap
         *            {@link Bitmap}
         */
        public void takePic(Bitmap mBitmap)
        {

        }

        /**
         * 
         * 拍照
         * 
         * @param picBytes
         *            图片字节数组
         */
        public void takePic(byte[] picBytes)
        {

        }

        /**
         * 
         * 失败
         * 
         */
        public abstract void failed();
    }

    /**
     * 自定义运行时异常类 不支持的分辨率
     * 
     * @author res-qqlin
     * 
     */
    public class UnsupportRatioException extends RuntimeException
    {

        private static final long serialVersionUID = -5788724507077838493L;

        /**
         * 
         * 构造方法
         * 
         * @param msg
         *            异常信息
         */
        public UnsupportRatioException(String msg)
        {
            super(msg);
        }
    }

}
